// i.r(t), i.d(t, { default: () => SweepPanoTiling })
import SymbolList from "../SymbolList"
import { TilingError } from "../exception/tile.exception"

import Logger from "../utils/Logger"
import * as THREE from "three"
import ObjectPool from "../observer/object.pool"
import Vectors from "../three/Vectors"
var u = {
  0: "PreOrder",
  1: "PostOrder",
  PreOrder: 0,
  PostOrder: 1
}

class TreeNode {
  tree: any
  parent: any
  children: any[]
  id: number
  static _id: any
  extra: {}
  level: number
  constructor(e, t) {
    ;(this.tree = e), (this.parent = t), (this.children = []), (this.id = ++TreeNode._id), (this.extra = {}), (this.level = -1)
  }
}
TreeNode._id = 0
class TileTree {
  levels: any
  tileSize: any
  root: any
  allNodes: any[]
  constructor(e, t) {
    ;(this.levels = t), (this.tileSize = e), (this.root = null), (this.allNodes = []), m(this)
  }
  getSizeForLevel(e, t) {
    return Math.pow(2, t) * e
  }
  getSubNode(e, t, i) {
    ;(!t || e < this.tileSize) && (t = 0), (!i || e < this.tileSize) && (i = 0), e < this.tileSize && (e = this.tileSize)
    const s = p(this.tileSize, e)
    return f(this.root, 0, s, t, i)
  }
  deleteAllNodes() {
    this.depthFirst(
      function (e, t, i, s, n) {
        for (let t = 0; t < e.children.length; t++) {
          const i = e.children[t]
          i && ((i.parent = null), (i.tree = null)), (e.children[t] = null)
        }
        e.children.length = 0
      }.bind(this),
      null,
      u.PostOrder
    )
    for (let e = 0; e < this.allNodes.length; e++) {
      const t = this.allNodes[e]
      t && ((t.parent = null), (t.tree = null)), (this.allNodes[e] = null)
    }
    ;(this.allNodes.length = 0), (this.root = null)
  }
  breadthFirst(e) {
    const t = !!(e = e || {}).nullLevelEnd,
      i = e.maxLevel,
      s = e.minLevel,
      n = e.callback,
      r = e.saveVisited,
      o = [],
      a = new TreeNode(this, null)
    let h = 0
    if (this.root)
      for (o.push(this.root), o.push(a); o.length > 0 && !(i && h > i); ) {
        const e = o.shift()
        if (e)
          if (e === a) (!s || h >= s) && (n && t && n(), r && t && r.push(null)), o.length > 0 && o.push(a), h++
          else {
            if (e.children) for (const t of e.children) t && o.push(t)
            const t = this.getFaceIndexFromNode(e)
            ;(!s || h >= s) && (n && n(e, h, t), r && r.push(e))
          }
      }
  }
  getFaceIndexFromNode(e) {
    if (!e) return -1
    let t = 1,
      i = e,
      s = 0,
      n = 0
    for (;;) {
      const e = i.parent
      if (!e) break
      let r = -1
      for (let t = 0; t < e.children.length; t++) e.children[t] === i && (r = t)
      ;(s = (r % 2) * t + s), (n = Math.floor(r / 2) * t + n), (t *= 2), (i = e)
    }
    return n * t + s
  }
  depthFirst(e, t, i) {
    g(this.root, 0, 0, 0, e, t, i, this.tileSize)
  }
}
const p = function (e, t) {
    let i = 0
    for (t < e && (t = e); !((t /= 2) < e); ) i++
    return i
  },
  g = function (e, t, i, s, n, r, o, a) {
    if (!e) return
    const h = 2 * s + i
    if (((o = o || u.PreOrder) === u.PreOrder && (n && n(e, t, h, i, s), r && r.push(e)), !e.children || 0 === e.children.length)) return
    const l = 2 * s,
      d = 2 * i
    for (let i = 0; i < 2; i++) for (let s = 0; s < 2; s++) g(e.children[2 * s + i], t + 1, d + i, l + s, n, r, o, a)
    o === u.PostOrder && (n && n(e, t, h, i, s), r && r.push(e))
  },
  m = e => {
    e.root = w(e, null, 0)
  },
  w = (e, t, i) => {
    if (i > e.levels) return null
    const s = new TreeNode(e, t)
    ;(s.level = i), e.allNodes.push(s)
    for (let t = 0; t < 4; t++) {
      const n = w(e, s, i + 1)
      n && (s.children[t] = n)
    }
    return s
  },
  f = (e, t, i, s, n) => {
    if (!e) return null
    if (0 === i) return e
    {
      if (!e.children || 0 === e.children.length) return null
      const r = Math.pow(2, i) / 2,
        o = s % r,
        a = n % r,
        h = 2 * Math.floor(n / r) + Math.floor(s / r),
        l = e.children[h]
      return f(l, t + 1, i - 1, o, a)
    }
  }

import T from "../math/14844"
import PanoRenderer from "../renderer/PanoRenderer"
import PanoQualityManager from "../utils/PanoQualityManager"
import { getCubeFace } from "../utils/cube.util"
import PersistentStorage from "../storage/PersistentStorage"
import TilePrioritizer from "../other/79-27079"
var v = {
  0: "Base",
  1: "Remaining",
  Base: 0,
  Remaining: 1
}

class TileUploadQueue {
  forceQueue: any[]
  uploadQueues: {}
  panoLODDescriptors: {}
  constructor() {
    ;(this.forceQueue = []), (this.uploadQueues = {}), (this.panoLODDescriptors = {})
  }
  addToForceQueue(e) {
    this.forceQueue.push(e)
  }
  addToPanoQueue(e, t) {
    this.getUploadQueueForPano(e).push(t)
  }
  insertSortedIntoPanoQueue(e, t, i) {
    const s = this.getUploadQueueForPano(t.id)
    TilePrioritizer.insertSortedPanoTile(s, e, t, i)
  }
  sortQueue(e, t) {
    const i = this.getUploadQueueForPano(e.id)
    TilePrioritizer.sortTiles(i, e, t)
  }
  getUploadQueueForPano(e) {
    let t = this.uploadQueues[e]
    return t || ((t = []), (this.uploadQueues[e] = t)), t
  }
  hasQueuedTiles() {
    if (this.forceQueue.length > 0) return !0
    for (const e in this.uploadQueues) {
      const t = this.getUploadQueueForSweep(e)
      if (t && t.length > 0) return !0
    }
    return !1
  }
  getUploadQueueForSweep(e) {
    let t = this.uploadQueues[e]
    return t || ((t = []), (this.uploadQueues[e] = t)), t
  }
  getTopUploadQueue(e) {
    let t = null
    for (let i = v.Base; i <= v.Remaining; i++)
      for (const s of e)
        if (((t = this.getUploadQueueForSweep(s.id)), t.length > 0))
          switch (i) {
            case v.Base:
              if (0 === t[0].level) return t
              break
            case v.Remaining:
              return t
          }
    return null
  }
  processNextQueueItem(e) {
    const t = e.shift()
    return t ? ((t.uploadQueued = !1), t) : null
  }
  getNextFromUploadQueue(e) {
    if (this.forceQueue.length > 0) return this.processNextQueueItem(this.forceQueue)
    const t = this.getTopUploadQueue(e)
    return t && t.length > 0 ? this.processNextQueueItem(t) : null
  }
  peekNextFromUploadQueue(e) {
    if (this.forceQueue.length > 0) return this.forceQueue[0]
    const t = this.getTopUploadQueue(e)
    return t && t.length > 0 ? t[0] : null
  }
  clearAllQueuedUploads() {
    this.clearAllUploadQueues(null, 0)
  }
  clearAllUploadQueues(e, t = 0) {
    if (e) this.clearUploadQueue(this.getUploadQueueForSweep(e), t), this.clearUploadQueue(this.forceQueue, t, e)
    else {
      for (const e in this.uploadQueues) this.clearUploadQueue(this.getUploadQueueForSweep(e), t)
      this.clearUploadQueue(this.forceQueue, t)
    }
  }
  clearUploadQueue(e, t = 0, i?) {
    let s = 0
    for (; s < e.length; ) {
      const n = e[s]
      ;(!i || (i && i === n.sweepId)) && n.level >= t ? ((n.uploadQueued = !1), e.splice(s, 1)) : s++
    }
  }
  resetPanoLODDescriptors(e) {
    const t = this.getPanoLODDescriptors(e)
    for (const e in t)
      if (t.hasOwnProperty(e)) {
        const i = t[e]
        ;(i.uploadCount = 0), (i.uploadAttempts = 0)
      }
  }
  getPanoLODDescriptor(e, t) {
    const i = this.getPanoLODDescriptors(e)
    let s = i[t]
    return s || ((s = { uploadCount: 0, uploadAttempts: 0 }), (i[t] = s)), s
  }
  getPanoLODDescriptors(e) {
    let t = this.panoLODDescriptors[e]
    return t || ((t = {}), (this.panoLODDescriptors[e] = t)), t
  }
}
import { PanoRenderTargetResizedMessage } from "../message/pano.message"
import funcUtil from "../utils/func.util"
import { ScheduleTaskCommand } from "../command/task.command"
import resolutionEnum from "../enum/resolution.enum"
const L = new Logger("tiles"),
  F = configConst.tileConfig.uploadIntervalDelay

class TiledPanoRenderer extends PanoRenderer {
  cwfRenderer: any
  panoQualityManager: any
  tileDownloader: any
  sweepData: any
  cameraData: any
  tilingSettings: any
  broadcast: any
  persistentStorage: PersistentStorage
  activeSweeps: any[]
  sweepLoadHistory: any[]
  activeRenderTargetDescriptors: {}
  panoLoadPromises: {}
  panoLoadResolvers: {}
  panoLoadTimers: {}
  tileTrees: {}
  tileDirectory: {}
  zoomSweepRenderingDisabled: boolean
  zoomingActive: boolean
  zoomSweepId: any
  usingTileOverlay: boolean
  overlayTilesLoaded: boolean
  overlayTileBase: any
  overlayTilesBasic: {}
  overlayTilesEnhanced: {}
  tileUploadQueue: TileUploadQueue
  currentState: any
  renderTargetPool: ObjectPool
  engine: any
  zoomRenderTarget: any
  currentUploadPromise: any
  constructor(e, t, i, s, n, r, o, a) {
    super(e, n.getSweepList()),
      (this.cwfRenderer = t),
      (this.panoQualityManager = i),
      (this.tileDownloader = s),
      (this.sweepData = n),
      (this.cameraData = r),
      (this.tilingSettings = o),
      (this.broadcast = a),
      (this.persistentStorage = new PersistentStorage()),
      (this.activeSweeps = []),
      (this.sweepLoadHistory = []),
      (this.activeRenderTargetDescriptors = {}),
      (this.panoLoadPromises = {}),
      (this.panoLoadResolvers = {}),
      (this.panoLoadTimers = {}),
      (this.tileTrees = {}),
      (this.tileDirectory = {}),
      (this.zoomSweepRenderingDisabled = !1),
      (this.zoomingActive = !1),
      (this.zoomSweepId = null),
      (this.usingTileOverlay = !1),
      (this.overlayTilesLoaded = !1),
      (this.overlayTileBase = null),
      (this.overlayTilesBasic = {}),
      (this.overlayTilesEnhanced = {}),
      (this.tileUploadQueue = new TileUploadQueue()),
      (this.currentState = {
        direction: new THREE.Vector3(),
        position: new THREE.Vector3(),
        rotation: new THREE.Quaternion()
      }),
      (this.renderTargetPool = new ObjectPool(this.rtCompare)),
      this.tileDownloader.setModel(this.sweepListMap),
      this.tileDownloader.setLoadCallbacks(this.onTileDownLoaded.bind(this))
  }
  activate(e) {
    this.engine = e
  }
  rtCompare(e, t) {
    return e.object.height === t.size && e.object.width === t.size
  }
  getActivePanos() {
    const e = []
    for (const t of Object.keys(this.activeRenderTargetDescriptors)) {
      this.activeRenderTargetDescriptors[t] && e.push(t)
    }
    return e
  }
  init() {
    super.init()
    this.loadOverlayTiles()
  }
  highResRenderTarget(e, t) {
    if (e) {
      if (!t) throw new TilingError("Cannot activate zooming without sweepId!")
      ;(this.zoomingActive = !0), (this.zoomSweepId = t), this.copyTargetToZoom(t)
    } else (this.zoomingActive = !1), (this.zoomSweepId = null)
    if (t) {
      const e = this.getRenderTargetDescriptorForSweep(t)
      if (!e) throw new TilingError("Zooming at a null render target!")
      const i = this.zoomingActive ? this.zoomRenderTarget : e.object,
        s = i.width
      this.broadcast(new PanoRenderTargetResizedMessage(s, t, i))
    }
  }
  getCurrentPanoResolution() {
    const e = this.zoomingActive ? this.panoQualityManager.getZoomPanoSize() : this.panoQualityManager.getNavPanoSize()
    return PanoQualityManager.getPanoSizeClass(e)
  }
  beforeRender() {
    const e = this.sweepData.currentSweep,
      t = this.sweepData.transition.to
    this.updateState(this.cameraData.pose.position, this.cameraData.pose.rotation, e, t), this.updateUploadQueueProcessing()
  }
  activateSweep(e) {
    const t = this.sweepListMap.get(e)
    if (!t) throw (L.error(e, t), new TilingError("Invalid sweepId passed to TiledPanORenderer.activate()"))
    let i = this.panoLoadPromises[e]
    return (
      i ||
        ((this.panoLoadTimers[e] = window.setTimeout(() => {
          this.engine.broadcast(new LoadIndicatorMessage(!0))
        }, configConst.tileConfig.loadIndicatorDelay)),
        (i = new Promise((i, s) => {
          ;(this.panoLoadResolvers[e] = i),
            this.updateSweepState(e),
            this.activatePano(t),
            this.queueUploadForAllTiles(e, !0),
            this.tileDownloader.forceQueueTiles(t, PanoQualityManager.getPanoSize(resolutionEnum.BASE), this.currentState.direction, !0)
        })),
        (this.panoLoadPromises[e] = i)),
      super._activateSweep(e),
      i
    )
  }
  useTexture(e) {
    const t = this.getRenderTargetDescriptorForSweep(e)
    if (!t) throw (L.error(e), Error("Texture for sweep not activated before using"))
    const i = t.object.texture
    return super._useTexture(e), this.freeUnusedTextures(), this.zoomingActive ? this.zoomRenderTarget.texture : i
  }
  freeTexture(e, t = !1) {
    super.freeTexture(e, t), 0 === this.textureUsageCounter[e] && ((this.panoLoadPromises[e] = null), this.deactivatePano(e))
  }
  freeAllTextures(e = []) {
    const t = funcUtil.valToFalse(e),
      i = this.getActivePanos()
    for (const e of i) t[e] || this.freeTexture(e, !0)
  }
  enableUltraHighQualityMode(e) {
    this.setupZoomRenderTarget(),
      this.zoomSweepId && this.broadcast(new PanoRenderTargetResizedMessage(this.zoomRenderTarget.width, this.zoomSweepId, this.zoomRenderTarget))
  }
  resetRenderStatus(e, t, i, s) {
    let n
    s && (n = p(T.I_, s) + 1)
    const r = (e, s, n, r) => {
      i && (s.extra.tile.zoomUploaded = !1), t && (s.extra.tile.uploaded = !1)
    }
    for (let t = 0; t < T.Hq; t++) {
      this.getTileTree(e, t).breadthFirst({
        callback: r.bind(this, t),
        minLevel: n
      })
    }
  }
  copyBaseRenderStatusToZoomed(e) {
    const t = p(T.I_, this.panoQualityManager.getNavPanoSize()),
      i = (e, t, i, s) => {
        ;(t.extra.tile.zoomUploaded = t.extra.tile.uploaded), (t.extra.zoomCovered = t.extra.covered)
      }
    for (let s = 0; s < T.Hq; s++) {
      this.getTileTree(e, s).breadthFirst({
        callback: i.bind(this, s),
        maxLevel: t
      })
    }
  }
  renderPanoTiles(e, t, i, s) {
    const n = []
    ;(this.zoomRenderTarget && this.zoomRenderTarget.width === this.panoQualityManager.getZoomPanoSize()) ||
      this.zoomSweepRenderingDisabled ||
      this.setupZoomRenderTarget(),
      (t = t || this.currentState.direction || Vectors.FORWARD)
    const r = this.getRenderTargetDescriptorForSweep(e)
    if (!this.isRenderTargetDescriptorValid(r)) throw new TilingError("PanoRenderer.renderPanoTiles() -> Cannot render to a pano that is not activated.")
    for (let t = 0; t < T.Hq; t++) {
      const r = this.getTileTree(e, t)
      ;(n.length = 0), r.breadthFirst({ saveVisited: n })
      for (let e = 0; e < n.length; e++) {
        const t = n[e]
        this.queueUploadForTile(t.extra.tile, !1, s || (0 === e && i))
      }
    }
  }
  clearAllQueuedUploads() {
    this.tileUploadQueue.clearAllUploadQueues(null, 0)
  }
  clearAllQueuedUploadsForPano(e) {
    this.tileUploadQueue.clearAllUploadQueues(e, 0)
  }
  updateState(e, t, i, s) {
    const n = s || i || this.currentState.sweepId,
      r = n ? this.sweepListMap.get(n) : null
    this.updatePositionState(e),
      this.updateRotationState(t),
      n && this.updateSweepState(n),
      r &&
        this.tileDownloader.tilePrioritizer &&
        this.tileDownloader.tilePrioritizer.updateCriteria(r, e, this.currentState.direction, this.currentState.rotation)
  }
  updatePositionState(e) {
    this.currentState.position.copy(e)
  }
  updateRotationState(e) {
    if ((this.currentState.rotation.copy(e), this.currentState.direction.copy(Vectors.FORWARD).applyQuaternion(e), this.tileUploadQueue.hasQueuedTiles()))
      for (const e of this.activeSweeps) this.tileUploadQueue.sortQueue(e, this.currentState.direction)
  }
  updateSweepState(e) {
    this.currentState.sweepId = e
  }
  activatePano(e) {
    this.tileUploadQueue.clearAllQueuedUploads()
    const t = this.panoQualityManager.getMaxPossiblePanoSize()
    for (let i = 0; i < T.Hq; i++) this.initTileTree(e.id, i, t)
    this.linkAllTilesAndNodes(e)
    let i = this.getRenderTargetDescriptorForSweep(e.id)
    if (!i) {
      const t = this.panoQualityManager.getNavPanoSize()
      if (((i = this.renderTargetPool.get({ size: t })), !i)) {
        const e = this.cwfRenderer.initRenderTargetCube(t)
        ;(i = this.renderTargetPool.add(e)), (i.extra = {}), (i.extra.size = e.width)
      }
      ;(i.extra.sweep = e),
        (i.extra.sweepindex = e.index),
        this.setRenderTargetDescriptorForSweep(e.id, i),
        this.tileUploadQueue.resetPanoLODDescriptors(e.id),
        this.resetUploadState(e.id, !0, !0)
    }
    return this.updateActiveSweeps(e), i.object
  }
  deactivatePano(e) {
    const t = this.getRenderTargetDescriptorForSweep(e)
    t &&
      this.isRenderTargetDescriptorValid(t) &&
      (this.renderTargetPool.free(t.object),
      this.setRenderTargetDescriptorForSweep(e, null),
      this.updateActiveSweeps(),
      this.tileUploadQueue.clearAllUploadQueues(e),
      this.tileUploadQueue.resetPanoLODDescriptors(e),
      this.clearCachedTileData())
  }
  clearCachedTileData() {
    for (let e = this.sweepLoadHistory.length - 1; e >= 0; e--) {
      let t = !1
      const i = this.sweepLoadHistory[e]
      if (i) {
        for (const e of this.activeSweeps)
          if (i === e.id) {
            t = !0
            break
          }
        !t &&
          this.checkTileTreeInitialized(i) &&
          (this.clearTileState(i, !0, !0),
          this.deleteTileTrees(i),
          this.deleteTileDirectoryEntries(i),
          this.tileDownloader.deleteAllTileDownloadDescriptors(i),
          (this.sweepLoadHistory[e] = null))
      }
    }
    this.updateSweepLoadHistory()
  }
  updateActiveSweeps(e?) {
    const t = this.persistentStorage.getArray("updateActiveSweeps:tempSweeps")
    t.length = 0
    for (const i of this.activeSweeps) {
      const s = this.getRenderTargetDescriptorForSweep(i.id)
      ;(e && i.id === e.id) || !this.isRenderTargetDescriptorValid(s) || t.push(i)
    }
    e && t.unshift(e), (this.activeSweeps.length = 0), this.activeSweeps.push(...t)
  }
  queueUploadForAllTiles(e, t = !0) {
    ;(this.zoomRenderTarget && this.zoomRenderTarget.width === this.panoQualityManager.getZoomPanoSize()) ||
      this.zoomSweepRenderingDisabled ||
      this.setupZoomRenderTarget()
    const i = this.getRenderTargetDescriptorForSweep(e)
    if (!this.isRenderTargetDescriptorValid(i)) throw new TilingError("queueUploadForAllTiles() -> Cannot render to a pano that is not activated.")
    const s = this.persistentStorage.getArray("queueUploadForAllTiles:nodeList")
    for (let i = 0; i < T.Hq; i++) {
      const n = this.getTileTree(e, i)
      ;(s.length = 0), n.breadthFirst({ saveVisited: s })
      for (const e of s) this.queueUploadForTile(e.extra.tile, !1, 0 === e.level && t)
    }
  }
  onTileDownLoaded(e) {
    if (!e.sweep) return
    const t = p(T.I_, e.panoSize),
      i = this.getTileDirectoryEntry(e.sweep.id, e.face, t, e.faceTileIndex)
    this.updateUploadDescriptorFromDownloadDescriptor(i, e), this.updateSweepLoadHistory(i.sweepId)
    const s = this.getRenderTargetDescriptorForSweep(i.sweepId)
    if (this.isRenderTargetDescriptorValid(s)) {
      const e = this.getTileTree(i.sweepId, i.face).getSubNode(i.panoSize, i.tileX, i.tileY)
      e && (this.linkTileAndNode(i, e), this.queueUploadForTile(i, !0))
    }
  }
  updateUploadDescriptorFromDownloadDescriptor(e, t) {
    ;(e.downloaded = !0),
      (e.image = t.image),
      (e.panoSize = t.panoSize),
      (e.tileX = t.tileX),
      (e.tileY = t.tileY),
      (e.totalTiles = t.totalTiles),
      (e.tileIndex = t.tileIndex),
      (e.faceTileIndex = t.faceTileIndex),
      (e.face = t.face),
      (e.cubeFace = getCubeFace(t.face)),
      t.sweep && (e.sweepId = t.sweep.id),
      (e.tileSize = t.tileSize),
      e.direction.copy(t.direction),
      (e.node = null),
      (e.level = p(T.I_, e.panoSize))
  }
  updateSweepLoadHistory(e?) {
    const t = this.persistentStorage.getArray("updateSweepLoadHistory:tempHistory")
    t.length = 0
    for (const i of this.sweepLoadHistory) i && (!e || (e && e !== i)) && t.push(i)
    ;(this.sweepLoadHistory.length = 0), e && this.sweepLoadHistory.push(e), this.sweepLoadHistory.push(...t)
  }
  onPanoRendered(e, t, i, s?) {
    clearTimeout(this.panoLoadTimers[e]), this.engine.broadcast(new LoadIndicatorMessage(!1))
    const n = this.panoLoadResolvers[e],
      r = this.activeRenderTargetDescriptors[e]
    r && r.object && n(r.object.texture)
  }
  getRenderTargetDescriptorForSweep(e) {
    return this.activeRenderTargetDescriptors[e]
  }
  setRenderTargetDescriptorForSweep(e, t) {
    this.activeRenderTargetDescriptors[e] = t
  }
  isRenderTargetDescriptorValid(e) {
    return !!e && !!e.object
  }
  isSweepZoomed(e) {
    return this.zoomingActive && this.zoomSweepId === e
  }
  initTileTree(e, t, i) {
    let s = this.tileTrees[e]
    s || ((s = []), (this.tileTrees[e] = s))
    let n = s[t]
    if (!n) {
      const e = p(T.I_, i)
      ;(n = new TileTree(T.I_, e)), (s[t] = n)
    }
  }
  getTileTrees(e) {
    const t = this.tileTrees[e]
    if (!t) throw new TilingError("TiledPanoRenderer.getTileTrees() -> Tree array not yet initialized!")
    return t
  }
  checkTileTreeInitialized(e) {
    return !!this.tileTrees[e]
  }
  getTileTree(e, t) {
    const i = this.getTileTrees(e)[t]
    if (!i) throw new TilingError("TiledPanoRenderer.getTileTree() -> Tree not yet initialized!")
    return i
  }
  deleteTileTrees(e) {
    const t = this.getTileTrees(e)
    for (let e = 0; e < T.Hq; e++) {
      const i = t[e]
      i && i.deleteAllNodes()
    }
    ;(this.tileTrees[e] = null), delete this.tileTrees[e]
  }
  clearTileState(e, t = !1, i = !1) {
    const s = (e, s, n, r) => {
      i && (s.extra.tile.image = null),
        t && ((s.extra.tile.uploaded = !1), (s.extra.tile.downloaded = !1), (s.extra.tile.zoomUploaded = !1), (s.extra.tile.uploadAttempted = !1))
    }
    for (let t = 0; t < T.Hq; t++) {
      const i = this.getTileTree(e, t)
      i &&
        i.breadthFirst({
          callback: s.bind(this, t),
          maxLevel: p(T.I_, this.panoQualityManager.getZoomPanoSize())
        })
    }
  }
  resetUploadState(e, t, i) {
    const s = (e, s, n, r) => {
      ;(s.extra.tile.zoomUploaded = !i && s.extra.tile.zoomUploaded), (s.extra.tile.uploaded = !t && s.extra.tile.uploaded)
    }
    for (let t = 0; t < T.Hq; t++) {
      this.getTileTree(e, t).breadthFirst({
        callback: s.bind(this, t),
        minLevel: 0
      })
    }
  }
  anyUploaded(e) {
    if (!e) return !1
    if (e.extra.tile && this.isTileUploaded(e.extra.tile)) return !0
    if (e.children) for (const t of e.children) if (this.anyUploaded(t)) return !0
    return !1
  }
  linkTileAndNode(e, t) {
    ;(t.extra.tile = e), (e.node = t)
  }
  linkAllTilesAndNodes(e) {
    const t = (t, i, s, n, r) => {
      const o = this.getTileDirectoryEntry(e.id, i, n, r)
      this.linkTileAndNode(o, s)
    }
    for (let i = 0; i < T.Hq; i++) {
      const s = this.getTileTree(e.id, i)
      s.breadthFirst({ callback: t.bind(this, s, i) })
    }
  }
  getTileDirectoryEntry(e, t, i, s) {
    let n = this.tileDirectory[e]
    n || ((n = {}), (this.tileDirectory[e] = n))
    const r = 16384 * t + 1024 * i + s
    let o = n[r]
    return (
      o ||
        ((o = {
          downloaded: !1,
          uploaded: !1,
          uploadAttempted: !1,
          zoomUploaded: !1,
          uploadQueued: !1,
          image: null,
          panoSize: -1,
          tileX: -1,
          tileY: -1,
          totalTiles: -1,
          tileIndex: s,
          faceTileIndex: -1,
          face: t,
          cubeFace: -1,
          sweepId: e,
          tileSize: -1,
          direction: new THREE.Vector3(),
          node: null,
          level: i
        }),
        (n[r] = o)),
      (o._tileKey = r),
      o
    )
  }
  deleteTileDirectoryEntries(e) {
    ;(this.tileDirectory[e] = null), delete this.tileDirectory[e]
  }
  isTileUploaded(e) {
    return this.isSweepZoomed(e.sweepId) ? e.zoomUploaded : e.uploaded
  }
  setUploaded(e, t) {
    this.isSweepZoomed(e.sweepId) ? (e.zoomUploaded = t) : (e.uploaded = t)
  }
  queueUploadForTile(e, t, i?) {
    const s =
        !e.downloaded || (e.uploadQueued && !i) || this.isTileUploaded(e) || (e.panoSize > this.panoQualityManager.getNavPanoSize() && !this.zoomingActive),
      n = this.getRenderTargetDescriptorForSweep(e.sweepId)
    !s &&
      n &&
      this.isRenderTargetDescriptorValid(n) &&
      (i
        ? this.uploadTile(e)
        : (0 === p(T.I_, e.panoSize)
            ? this.tileUploadQueue.addToForceQueue(e)
            : t && this.currentState.direction
            ? this.tileUploadQueue.insertSortedIntoPanoQueue(e, n.extra.sweep, this.currentState.direction)
            : this.tileUploadQueue.addToPanoQueue(e.sweepId, e),
          (e.uploadQueued = !0)))
  }
  uploadTile(e) {
    const t = this.persistentStorage.get("uploadTile:tempTileTexture", () => ({})),
      i = this.tileUploadQueue.getPanoLODDescriptor(e.sweepId, e.panoSize),
      s = this.getRenderTargetDescriptorForSweep(e.sweepId)
    if (!s || !e.image || !this.isRenderTargetDescriptorValid(s)) return
    let n = s.object,
      o = s.extra.size
    if (
      (this.isSweepZoomed(e.sweepId) && ((n = this.zoomRenderTarget), (o = this.panoQualityManager.getZoomPanoSize())),
      this.isTileUploaded(e) || this.anyUploaded(e.node))
    )
      this.setUploaded(e, !1)
    else {
      const s = e.tileX * e.tileSize,
        a = e.tileY * e.tileSize,
        h = (e.tileSize / e.panoSize) * o,
        d = (s / e.panoSize) * o,
        c = (a / e.panoSize) * o
      let u = t[e.tileSize]
      if (
        (t[e.tileSize] ||
          ((u = new THREE.Texture()),
          (u.generateMipmaps = !1),
          (u.minFilter = THREE.LinearFilter),
          (u.flipY = !1),
          this.cwfRenderer.initSizedTexture2D(e.tileSize, u),
          (t[e.tileSize] = u)),
        this.cwfRenderer.uploadTexture2D(e.image, u, 0, 0),
        this.cwfRenderer.renderToCubeMap(u, n, e.tileSize, e.tileSize, 0, 0, e.tileSize, e.tileSize, d, c, h, h, e.cubeFace),
        configConst.tileConfig.overlayStyle > 0)
      ) {
        const t = 1 === configConst.tileConfig.overlayStyle ? this.overlayTilesBasic : this.overlayTilesEnhanced
        this.cwfRenderer.renderToCubeMap(
          t[e.panoSize],
          n,
          e.tileSize,
          e.tileSize,
          0,
          0,
          e.tileSize,
          e.tileSize,
          d,
          c,
          h,
          h,
          e.cubeFace,
          THREE.NormalBlending,
          !0,
          0.5
        )
      }
      i.uploadCount++, this.setUploaded(e, !0)
    }
    e.uploadAttempted || i.uploadAttempts++,
      (e.uploadAttempted = !0),
      i.uploadAttempts === e.totalTiles && this.onPanoRendered(e.sweepId, e.panoSize, e.totalTiles)
  }
  updateUploadQueueProcessing() {
    if (!this.currentUploadPromise && (this.overlayTilesLoaded || !this.usingTileOverlay)) {
      const e = new ScheduleTaskCommand(
        "pano/tiling/upload",
        () =>
          this.engine
            .after(phaseState.End)
            .then(() => this.processUploadQueue(this.tilingSettings.highResUploadsPerFrame, this.tilingSettings.uploadsPerFrame)),
        F
      )
      this.currentUploadPromise = this.engine.commandBinder.issueCommand(e).then(async e => {
        await e.promise, (this.currentUploadPromise = null)
      })
    }
  }
  processUploadQueue(e = 1, t) {
    let i = 0,
      s = 0,
      n = null
    for (; (n = this.tileUploadQueue.getNextFromUploadQueue(this.activeSweeps)); ) {
      const r = this.getRenderTargetDescriptorForSweep(n.sweepId)
      if (
        !((n.panoSize > this.panoQualityManager.getNavPanoSize() && !this.zoomingActive) || !this.isRenderTargetDescriptorValid(r)) &&
        (this.uploadTile(n), (i += 0 !== n.level ? 1 : 0), (s += 0 === n.level ? 1 : 0), s >= t || i >= e)
      )
        break
    }
  }
  loadOverlayTiles() {
    if (0 !== configConst.tileConfig.overlayStyle) {
      let e = 0
      const t = [],
        s = (i, s, n) => {
          let r = new THREE.Texture()
          ;(r.generateMipmaps = !1),
            (r.minFilter = THREE.LinearFilter),
            (r.flipY = !1),
            s
              ? (r = i[s] = this.cwfRenderer.initSizedTexture2D(T.I_, r))
              : ((this.overlayTileBase = this.cwfRenderer.initSizedTexture2D(T.I_, r)), (r = this.overlayTileBase)),
            this.cwfRenderer.uploadTexture2D(n, r, 0, 0),
            e++,
            e >= t.length && (this.overlayTilesLoaded = !0)
        }
      switch (configConst.tileConfig.overlayStyle) {
        case 1:
          t.push(["./images/outlineBasic512.png", this.overlayTilesBasic, 256]),
            t.push(["./images/outlineBasic512.png", this.overlayTilesBasic, 512]),
            t.push(["./images/outlineBasic1024.png", this.overlayTilesBasic, 1024]),
            t.push(["./images/outlineBasic2048.png", this.overlayTilesBasic, 2048]),
            t.push(["./images/outlineBasic4096.png", this.overlayTilesBasic, 4096])
          break
        case 2:
          t.push(["./images/outlineEnhanced512.png", this.overlayTilesEnhanced, 256]),
            t.push(["./images/outlineEnhanced512.png", this.overlayTilesEnhanced, 512]),
            t.push(["./images/outlineEnhanced1024.png", this.overlayTilesEnhanced, 1024]),
            t.push(["./images/outlineEnhanced2048.png", this.overlayTilesEnhanced, 2048]),
            t.push(["./images/outlineEnhanced4096.png", this.overlayTilesEnhanced, 4096])
      }
      t.forEach(e => {
        const t = document.createElement("img")
        ;(t.crossOrigin = "anonymous"),
          (t.src = e[0]),
          (t.onload = () => {
            s.call(this, e[1], e[2], t)
          })
      }),
        (this.usingTileOverlay = !0)
    } else this.usingTileOverlay = !1
  }
  copyTargetToZoom(e) {
    if (!this.zoomingActive) return
    const t = this.getRenderTargetDescriptorForSweep(e)
    if (!t) throw new TilingError("Error in copying a null render target to a zoomed target")
    const i = t.object
    this.cwfRenderer.copyCubemap(i.texture, this.zoomRenderTarget), this.copyBaseRenderStatusToZoomed(e)
  }
  setupZoomRenderTarget() {
    if (this.panoQualityManager.getZoomPanoSize() >= this.panoQualityManager.getNavPanoSize()) {
      if (this.zoomRenderTarget && this.zoomRenderTarget.width === this.panoQualityManager.getZoomPanoSize()) return
      const e = this.zoomRenderTarget
      ;(this.zoomRenderTarget = this.cwfRenderer.initRenderTargetCube(this.panoQualityManager.getZoomPanoSize())),
        e &&
          (this.cwfRenderer.copyCubemap(e.texture, this.zoomRenderTarget),
          e.texture.dispose(),
          (e.texture.version = 0),
          this.cwfRenderer.deallocateCubeTexture(e.texture),
          (e.texture = null)),
        (this.zoomSweepRenderingDisabled = !1)
    } else this.zoomSweepRenderingDisabled = !0
  }
}
import { TileDownloadedMessage } from "../message/tile.message"
const G = new Logger("tile-downloader")
class TileDownloader {
  urls: ModelDataModule["signedUrls"]
  panoQualityManager: PanoQualityManager
  tilePrioritizer: TilePrioritizer
  settings: TilingSettings
  persistentStorage: PersistentStorage
  downloadDescriptors: {}
  priorityQueue: any[]
  forceQueue: any[]
  activeDownloads: any[]
  lastPrioritizedTime: number
  processPriorityQueue: boolean
  processQueuePromise: any
  sweepListMap: any
  engine: EngineContext
  onTileDownloaded: any
  onPanoDownloaded: any
  constructor(e, t, i, s) {
    ;(this.urls = e),
      (this.panoQualityManager = t),
      (this.tilePrioritizer = i),
      (this.settings = s),
      (this.persistentStorage = new PersistentStorage()),
      (this.downloadDescriptors = {}),
      (this.priorityQueue = []),
      (this.forceQueue = []),
      (this.activeDownloads = []),
      (this.lastPrioritizedTime = Date.now()),
      (this.processPriorityQueue = !0)
  }
  init() {}
  render() {
    this.update()
  }
  update() {
    this.processQueue(this.forceQueue, !1),
      this.processPriorityQueue &&
        (!this.processQueuePromise &&
          this.sweepListMap &&
          this.activeDownloads.length < this.settings.concurrentDownloads &&
          Date.now() - this.lastPrioritizedTime > 200 &&
          (this.processQueuePromise = this.engine.commandBinder
            .issueCommand(
              new ScheduleTaskCommand(
                "pano/tiling/queue-download",
                () => {
                  this.queuePrioritizedTiles(this.sweepListMap), (this.lastPrioritizedTime = Date.now())
                },
                100
              )
            )
            .then(async e => {
              await e.promise, (this.processQueuePromise = null)
            })),
        this.processQueue(this.priorityQueue, !1))
  }
  dispose() {}
  activate(e) {
    this.engine = e
  }
  deactivate(e) {}
  setModel(e) {
    this.setSweeps(e)
  }
  setRestrictedSweeps(e) {
    if (this.sweepListMap && this.tilePrioritizer) {
      const t = []
      for (const i of e) t.push(this.sweepListMap.get(i))
      this.tilePrioritizer.setUpcomingSweeps(t), this.clearFromAllQueuesBySweep(e)
    }
  }
  clearRestrictedSweeps() {
    this.tilePrioritizer && this.tilePrioritizer.clearUpcomingSweeps()
  }
  setLoadCallbacks(e, t) {
    ;(this.onTileDownloaded = e), (this.onPanoDownloaded = t)
  }
  setSweeps(e) {
    this.sweepListMap = e
  }
  getNonDownloadedTiles(e, t, i) {
    i.length = 0
    const s = this.getTileDownloadDescriptors(e, t)
    for (const e of s) !e || (e.status !== tileStatus.None && e.status !== tileStatus.Queued) || i.push(e)
  }
  forceQueueTiles(e, t, i, s, n, r) {
    const o = this.persistentStorage.getArray("forceQueueTiles:remaining"),
      a = this.persistentStorage.getArray("forceQueueTiles:matching"),
      h = this.persistentStorage.getArray("forceQueueTiles:toDownload")
    if ((this.getNonDownloadedTiles(e, t, o), (h.length = 0), o.length > 0)) {
      TilePrioritizer.sortTiles(o, e, i), (a.length = 0), T.oU(e, t, i, a, !0)
      for (const e of o) for (const t of a) e.face === t.face && e.faceTileIndex === t.faceTileIndex && h.push(e)
      this.forceQueue.push.apply(this.forceQueue, h),
        this.setStatusForAllDescriptors(this.forceQueue, tileStatus.ForceQueued),
        this.clearFromQueue(this.priorityQueue, tileStatus.ForceQueued, !1),
        s && this.processQueue(this.forceQueue, !0)
    }
  }
  clearForceQueue() {
    this.clearQueue(this.forceQueue)
  }
  queuePrioritizedTiles(e) {
    this.tilePrioritizer &&
      (this.clearQueue(this.priorityQueue),
      this.tilePrioritizer.filterAndPrioritize(this.priorityQueue, e, this),
      this.invalidateDuplicateEntries(this.priorityQueue),
      this.clearFromQueue(this.priorityQueue, tileStatus.None, !0),
      this.setStatusForAllDescriptors(this.priorityQueue, tileStatus.Queued),
      (this.lastPrioritizedTime = Date.now()))
  }
  clearQueue(e) {
    this.setStatusForAllDescriptors(e, tileStatus.None), (e.length = 0)
  }
  clearFromQueue(e, t, i) {
    for (let s = 0; s < e.length; s++) {
      const n = e[s]
      n && ((t === n.status && !i) || (t !== n.status && i)) && (e[s] = null)
    }
  }
  clearFromAllQueuesBySweep(e) {
    this.clearFromQueueBySweep(this.forceQueue, e), this.clearFromQueueBySweep(this.priorityQueue, e)
  }
  clearFromQueueBySweep(e, t) {
    const i = funcUtil.valToFalse(t)
    for (let t = 0; t < e.length; t++) {
      const s = e[t]
      s && s.sweep && (i[s.sweep.id] || (e[t] = null))
    }
  }
  setStatusForAllDescriptors(e, t) {
    for (const i of e) i && (i.status = t)
  }
  invalidateDuplicateEntries(e) {
    for (const t of e) t && (t.queuedCount = 0)
    for (let t = 0; t < e.length; t++) {
      const i = e[t]
      i && (i.queuedCount++, i.queuedCount > 1 && (e[t] = null))
    }
  }
  getTileDownloadDescriptors(e, t) {
    const i = this.getAllTileDownloadDescriptors(e.id)
    let s = i[t]
    return s || ((s = this.buildDownloadDescriptorArray(t)), (i[t] = s), this.initTileDownloadDescriptors(s, e, t)), s
  }
  getAllTileDownloadDescriptors(e) {
    let t = this.downloadDescriptors[e]
    return t || ((t = {}), (this.downloadDescriptors[e] = t)), t
  }
  deleteAllTileDownloadDescriptors(e) {
    ;(this.downloadDescriptors[e] = null), delete this.downloadDescriptors[e]
  }
  processQueue(e, t) {
    if ((this.cleanupActiveDownloads(), this.activeDownloads.length < this.settings.concurrentDownloads || t)) {
      const i = t ? e.length : this.settings.concurrentDownloads - this.activeDownloads.length
      let s = 0
      for (let t = 0; s < i && e.length > 0; t++) {
        const t = e.shift()
        t && (this.startDownload(t), s++)
      }
    }
  }
  async startDownload(e) {
    if (!this.urls) throw new Error("Can't call startDownload without signed urls")
    if (e.sweep) {
      const t = e.status === tileStatus.ForceQueued ? httpPriority.HIGHEST : httpPriority.MEDIUM
      e.status = tileStatus.Downloading
      this.checkRestrictedSweep(e.sweep.id) || G.warn("Downloading a tile that is not in restricted list"), this.activeDownloads.push(e)
      const i = await this.panoQualityManager.getTileUrl(e.sweep, e.panoSize, e.tileSize, e.tileIndex)
      this.urls
        .getImageBitmap(i, e.tileSize, e.tileSize, {
          maxRetries: 3,
          priority: t
        })
        .then(this.downloadComplete.bind(this, e), this.downloadFailed.bind(this, e))
    }
  }
  checkRestrictedSweep(e) {
    if (this.tilePrioritizer) {
      const t = this.tilePrioritizer.priorityCriteria.upcomingSweeps
      if (t) {
        let i = !1
        for (const s of t) s && s.id === e && (i = !0)
        return i
      }
      return !0
    }
    return !0
  }
  downloadFailed(e, t) {
    e.status = tileStatus.DownloadFailed
  }
  downloadComplete(e, t) {
    if (
      e.sweep &&
      ((e.status = tileStatus.Downloaded),
      (e.image = t),
      this.onTileDownloaded && this.onTileDownloaded(e),
      this.engine.broadcast(new TileDownloadedMessage()),
      this.isPanoDownloaded(e.sweep, e.panoSize))
    ) {
      const t = {
        sweep: e.sweep,
        tileSize: e.tileSize,
        panoSize: e.panoSize
      }
      this.onPanoDownloaded && this.onPanoDownloaded(t)
    }
  }
  cleanupActiveDownloads() {
    const e = this.persistentStorage.getArray("cleanupActiveDownloads:temp")
    e.length = 0
    for (const t of this.activeDownloads) t.status !== tileStatus.Downloaded && t.status !== tileStatus.DownloadFailed && e.push(t)
    ;(this.activeDownloads.length = 0), this.activeDownloads.push.apply(this.activeDownloads, e)
  }
  isPanoDownloaded(e, t) {
    const i = this.getTileDownloadDescriptors(e, t)
    if (!i || i.length <= 0) return !1
    for (const e of i) if (e && e.status !== tileStatus.Downloaded) return !1
    return !0
  }
  buildDownloadDescriptorArray(e) {
    const t = T.Tu(e),
      i = []
    for (let e = 0; e < t; e++) {
      const e = this.buildDownloadDescriptor()
      i.push(e)
    }
    return i
  }
  buildDownloadDescriptor() {
    return {
      sweep: null,
      panoSize: -1,
      tileSize: -1,
      tileIndex: -1,
      totalTiles: -1,
      faceTileIndex: -1,
      status: tileStatus.None,
      url: null,
      image: null,
      direction: new THREE.Vector3(),
      face: -1,
      cubeFace: -1,
      tileX: -1,
      tileY: -1,
      queuedCount: -1
    }
  }
  initTileDownloadDescriptors(e, t, i) {
    for (let s = 0; s < e.length; s++) {
      const n = e[s]
      n && this.initTileDownloadDescriptor(n, t, i, s)
    }
  }
  initTileDownloadDescriptor(e, t, i, s) {
    const n = i >= T.I_ ? T.I_ : i
    ;(e.face = T.R5(i, s)),
      (e.cubeFace = getCubeFace(e.face)),
      (e.sweep = t),
      (e.panoSize = i),
      (e.tileSize = n),
      (e.tileIndex = s),
      (e.totalTiles = T.Tu(i)),
      (e.status = tileStatus.None),
      (e.image = null),
      T.Tp(e.panoSize, e.tileIndex, e),
      T.P6(e.panoSize, e.tileSize, e.cubeFace, e.tileX, e.tileY, tileEnum.Center, 0, e.direction)
  }
}
import { RestrictedSweepsClearedMessge, RestrictedSweepsSetMessge } from "../message/sweeps.messge"
import ModelDataModule from "./ModelDataModule"
import SweepData from "../data/SweepData"
import SweepPanoModule from "./SweepPanoModule"
import CameraData from "../data/CameraData"
import OpenDeferred from "../utils/OpenDeferred"
import SettingsData from "../data/SettingsData"
import { TilingSetNavPanoSizeCommand, TilingOverrideWindowSizeCommand } from "../command/tiling.command"
import RaycasterData from "../data/RaycasterData"
import { PuckHoverMessage } from "../message/puck.message"
import $ from "../math/7170"
import { ZoomMessage } from "../message/zoom.message"
import { SwitchViewmodeMessage } from "../message/viewmode.message"
import configConst from "../constant/config.const"
import TilingSettings from "../settings/TilingSettings"
import keyConst from "../constant/key.const"
import { viewRange } from "../enum/viewmode.enum"
import { PlayerResizedMessage } from "../message/play.message"
import { phaseState } from "../enum/phase.enum"
import { LoadIndicatorMessage } from "../message/tool.message"
import tileEnum, { tileStatus } from "../enum/tile.enum"
import { httpPriority } from "../enum/http.enum"
import EngineContext from "../EngineContext"
import WebGLRendererModule from "./WebGLRendererModule"
const ee = new Logger("sweep-pano-tiling")
export default class SweepPanoTiling extends SweepPanoModule {
  preloadQuality: any
  preloadResolution: any
  handlePlayerResize: (e: any, t: any) => void
  cameraData: CameraData
  settingsData: SettingsData
  settings: TilingSettings
  qualityManager: PanoQualityManager
  tilePrioritizer: TilePrioritizer
  tileDownloader: TileDownloader
  constructor() {
    super(...arguments)
    this.name = "sweep-pano-tiling"
    this.preloadQuality = null
    this.preloadResolution = null
    this.handlePlayerResize = (e, t) => {
      const i = this.currentResolution()
      this.panoRenderer.panoQualityManager.setWindowSize(e.width, e.height)
      i !== this.currentResolution() && t && this.resetPano(t)
    }
  }
  async init(e, t: EngineContext) {
    const i = await t.getModule(ModelDataModule),
      a = await t.getModuleBySymbol<WebGLRendererModule>(SymbolList.WEBGL_RENDERER),
      h = await t.market.waitForData(SweepData)
    this.cameraData = await t.market.waitForData(CameraData)
    const l = await t.market.waitForData(RaycasterData)
    this.settingsData = await t.market.waitForData(SettingsData)
    const d = a.cwfRenderer
    const c = a.maxCubemapSize
    const u = i.getSignedUrls()
    this.settings = new TilingSettings()
    this.qualityManager = new PanoQualityManager(u, c, e.navPanoSize, e.zoomPanoSize)
    this.tilePrioritizer = new TilePrioritizer(this.qualityManager, this.settings, h, l)
    this.tileDownloader = new TileDownloader(u, this.qualityManager, this.tilePrioritizer, this.settings)
    this.panoRenderer = new TiledPanoRenderer(u, d, this.qualityManager, this.tileDownloader, h, this.cameraData, this.settings, e => t.broadcast(e))
    t.addComponent(this, this.tileDownloader)
    t.addComponent(this, this.panoRenderer)
    this.bindings.push(
      t.subscribe(ZoomMessage, e => {
        this.setTilingFOV()
      }),
      t.subscribe(PlayerResizedMessage, e => {
        this.handlePlayerResize(e, h.currentSweep), this.setTilingFOV()
      }),
      t.subscribe(RestrictedSweepsSetMessge, e => {
        this.tileDownloader.setRestrictedSweeps(e.sweepIds)
      }),
      t.subscribe(RestrictedSweepsClearedMessge, e => {
        this.tileDownloader.clearRestrictedSweeps()
      }),
      t.subscribe(PuckHoverMessage, e => {
        const t = h.getSweep(e.sweepId)
        this.setHoverPreloadSweep(e.hovered ? t : void 0)
      }),
      t.subscribe(SwitchViewmodeMessage, e => {
        this.tilePrioritizer.priorityCriteria.viewMode = e.toMode
      }),
      h.onPropertyChanged("transitionActive", () => {
        if (h.transition.to) {
          const e = h.getSweep(h.transition.to)
          this.handlePreloadQualityChange(e)
        }
      }),
      this.settingsData.onPropertyChanged(keyConst.settings.tiling.quality, () => {
        ;(this.preloadQuality = this.settingsData.tryGetProperty(keyConst.settings.tiling.quality, null)),
          this.qualityManager.overrideWindowMaximums(null !== this.preloadQuality),
          h.currentSweepObject && this.handlePreloadQualityChange(h.currentSweepObject)
      }),
      t.commandBinder.addBinding(TilingOverrideWindowSizeCommand, async e => {
        this.qualityManager.overrideWindowMaximums(e.ignoreWindowSize)
      }),
      t.commandBinder.addBinding(TilingSetNavPanoSizeCommand, async e => {
        this.qualityManager.updateNavPanoSize(e.navSize)
      })
    )
    this.setTilingFOV()
  }
  setTilingFOV() {
    const e = this.cameraData.fovX(),
      t = this.cameraData.fovY(),
      i = Math.max(e, t) * $.MN
    this.tilePrioritizer.setDownloadFOV(i)
  }
  getRenderer() {
    return this.panoRenderer
  }
  setHoverPreloadSweep(e) {
    this.tilePrioritizer && this.tilePrioritizer.setHoveredSweep(e)
  }
  async handlePreloadQualityChange(e) {
    if (null !== this.preloadQuality) {
      const t = await this.availableResolution(e, this.preloadQuality)
      this.preloadResolution = PanoQualityManager.getPanoSize(t)
    } else this.preloadResolution = null
    this.enableHighRes(!1)
  }
  enableHighRes(e = !0, t?) {
    const i = null !== this.preloadResolution ? this.preloadResolution : e ? this.qualityManager.getZoomPanoSize() : this.qualityManager.getNavPanoSize()
    this.tilePrioritizer.maxResolution !== i && ee.debug(`Setting max resolution: ${i}`),
      (this.tilePrioritizer.maxResolution = i),
      this.panoRenderer.highResRenderTarget(e, t)
  }
  async enableZooming(e, t) {
    if (e) {
      const e = PanoQualityManager.getPanoSizeClass(this.qualityManager.getZoomPanoSize())
      return (await this.requestResolution(t, e)) >= e
    }
    return this.enableHighRes(!1, t.id), this.resetPano(t.id), Promise.resolve(!1)
  }
  async requestResolution(e, t, i = viewRange.CurrentView, s = !1) {
    ;(this.settings.highResUploadsPerFrame = s ? configConst.tileConfig.maxHighResUploadsPerFrame : configConst.tileConfig.highResUploadsPerFrame),
      (this.settings.concurrentDownloads = s ? configConst.tileConfig.maxConcurrentDownloads : configConst.tileConfig.concurrentDownloads),
      (this.settings.downloadFullPano = i === viewRange.FullPanorama)
    const n = await this.availableResolution(e, t)
    return (
      n > resolutionEnum.HIGH && (this.qualityManager.overrideWindowMaximums(!0), this.panoRenderer.enableUltraHighQualityMode(e.id)),
      (this.currentResolution() < n || PanoQualityManager.getPanoSize(n) > this.qualityManager.getNavPanoSize()) &&
        (this.enableHighRes(!0, e.id), this.resetPano(e.id)),
      n
    )
  }
  waitForQueue(e, t, i = 1e3) {
    const s = new OpenDeferred(),
      n = () => {
        a.cancel(), s.notify(1), s.resolve()
      }
    let r = this.tilePrioritizer.getQualityQueueSize(e, t),
      o = window.setTimeout(() => {
        ee.debug(`Download queue ${e} timed out from inactivity after ${i}ms`), n()
      }, i)
    const a = this.tilePrioritizer.makeQueueSubscription(e, t, e => {
      if ((o && (window.clearTimeout(o), (o = 0), (r = e)), e > 0)) {
        const t = (r - e) / Math.max(r, 1)
        s.notify(t)
      } else n()
    })
    return s.promise()
  }
  resetSweep(e) {
    this.enableHighRes(!1, e), this.resetPano(e), this.settings.reset()
  }
  availableResolution(e, t) {
    return this.qualityManager.availableResolution(e, t).nativePromise()
  }
  currentResolution() {
    return this.panoRenderer.getCurrentPanoResolution()
  }
  resetPano(e) {
    this.panoRenderer.resetRenderStatus(e, !1, !0, this.panoRenderer.panoQualityManager.getNavPanoSize()),
      this.panoRenderer.clearAllQueuedUploadsForPano(e),
      this.panoRenderer.renderPanoTiles(e, null, !1, !1)
  }
}
